package com.efrei.schoolmanager.model;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.swing.table.DefaultTableModel;

import com.efrei.schoolmanager.model.obj.Classe;
import com.efrei.schoolmanager.model.obj.Eleve;
import com.efrei.schoolmanager.model.obj.Enseignant;
import com.efrei.schoolmanager.model.obj.Matiere;
import com.efrei.schoolmanager.model.obj.Note;
import com.efrei.schoolmanager.tools.Tools;

/**
 * Modele pour l'affichage du bulletin de notes
 * 
 * @author Julien
 * 
 */
public class ModeleBulletinNotes extends DefaultTableModel {
	private static final long serialVersionUID = 7575462792396004350L;

	// Liste contenant les titres de la table
	private ArrayList<String> entetes = new ArrayList<String>();

	// Liste contenant les valeurs de la table
	private ArrayList<ArrayList<String>> notes = new ArrayList<ArrayList<String>>();

	// Eleve selectionnee precedemment dans la dialog
	private final Eleve eleveSelectionne;

	// Classe selectionnee precedemment dans la dialog
	private final Classe classeActuelle;

	// Periode selectionnee (par defaut la periode courante)
	private int periodeSelectionnee = Tools.getPeriodeCourante();

	/**
	 * Constructeur du model
	 * 
	 * @param eleve
	 *            : l'eleve selectionnee
	 * @param classe
	 *            : la classe de l'eleve selectionne
	 */
	public ModeleBulletinNotes(Eleve eleve, Classe classe) {
		eleveSelectionne = eleve;
		classeActuelle = classe;
		traitementBulletinNotes();
	}

	/**
	 * Recupere et calcule les notes et moyennes Puis update le tableau
	 * 
	 * @param periode
	 */
	public void refresh(int periode) {
		periodeSelectionnee = periode;
		traitementBulletinNotes();
		fireTableStructureChanged();
	}

	/**
	 * Tri des notes, calculs des moyennes et mise en place des donnees
	 */
	private void traitementBulletinNotes() {

		// Remise a zero des donnees des donnes du model
		entetes = new ArrayList<String>();
		notes = new ArrayList<ArrayList<String>>();

		// Format de la note
		DecimalFormat df = new DecimalFormat("##.##");

		// ************************************************************************************************
		// Partie Eleve (pour le calcul de sa moyenne)
		// ************************************************************************************************

		// J'obtient toutes les notes de l'eleve selectionne
		Set<Note> notesEleve = eleveSelectionne.getNotes();

		// Hashmap qui contientrat les matieres avec la liste des notes
		// associees
		HashMap<Matiere, ArrayList<Float>> hashMat = new HashMap<Matiere, ArrayList<Float>>();

		// Parcours des notes de l'élève , je ne garde que celles de la periode
		// demandee et de sa classe
		for (Note note : notesEleve) {
			if (note.getClasse() == classeActuelle
					&& note.getPeriode() == periodeSelectionnee

			) {
				// Si les notes sont de sa classe et de la periode demandee
				// Je les ajoutes a la hashmap cree precedemment
				Matiere matiereTemp = note.getMatiere();
				if (hashMat.get(matiereTemp) == null) {
					ArrayList<Float> notesMatiere = new ArrayList<Float>();
					notesMatiere.add(note.getNote());
					hashMat.put(matiereTemp, notesMatiere);
				} else {
					ArrayList<Float> notesMatiere = hashMat.get(matiereTemp);
					hashMat.remove(matiereTemp);
					notesMatiere.add(note.getNote());
					hashMat.put(matiereTemp, notesMatiere);
				}
			}
		}

		// Ajout des entetes de la table
		entetes.add("Matiere");
		entetes.add("Enseignant");
		entetes.add("Moyenne Eleve");
		entetes.add("Moyenne Classe");
		entetes.add("Remarque de l'enseignant");

		// *********************************************************
		// Partie Classe (pour le calcul de moyenne de la classe)
		// *********************************************************

		// Obtention des notes de la classe de l'eleve selectionne
		Set<Note> setNotes = classeActuelle.getNotes();

		// Creation d'une map contenant pour une matiere donnee la liste des
		// notes associees
		HashMap<Matiere, ArrayList<Float>> mapMatieresNotesClasse = new HashMap<Matiere, ArrayList<Float>>();

		// Regroupement de toutes les notes de classe par matiere
		for (Note notesClasse : setNotes) {
			// Si la note correspond a la periode donnee j'ajoute la note a la
			// liste associee
			if (notesClasse.getPeriode() == periodeSelectionnee) {
				if (mapMatieresNotesClasse.get(notesClasse.getMatiere()) == null) {
					ArrayList<Float> listNotesClasse = new ArrayList<Float>();
					listNotesClasse.add(notesClasse.getNote());
					mapMatieresNotesClasse.put(notesClasse.getMatiere(),
							listNotesClasse);
				} else {
					mapMatieresNotesClasse.get(notesClasse.getMatiere()).add(
							notesClasse.getNote());
				}
			}

		}

		// Hashmap contenant les moyennes de la classe pour chaque matiere
		HashMap<Matiere, Float> mapMatieresMoyennesClasse = new HashMap<Matiere, Float>();

		// Obtention de toutes les matieres de la classe de l'eleve
		Set<Matiere> matieres = hashMat.keySet();

		// Calcul de la Moyenne de la classe pour toutes les matieres puis ajout
		// de cette moyenne dans la hashmap
		for (Matiere matiere : matieres) {
			float moyenne = 0;
			for (int i = 0; i < mapMatieresNotesClasse.get(matiere).size(); i++) {
				moyenne = moyenne + mapMatieresNotesClasse.get(matiere).get(i);
			}
			moyenne = moyenne / mapMatieresNotesClasse.get(matiere).size();
			mapMatieresMoyennesClasse.put(matiere, moyenne);
		}

		// Recuperation de la liste des enseignants responsable
		Map<Matiere, Enseignant> mapEnseignants = classeActuelle
				.getEnseignantsResponsables();

		// Ajout des moyennes classe et eleve dans les listes pour l'affichage
		for (Matiere matiere : matieres) {
			float moyenne = 0;
			ArrayList<String> ligne = new ArrayList<String>(4);
			ligne.add(matiere.getNom());
			ligne.add(mapEnseignants.get(matiere).getNom());
			for (int i = 0; i < hashMat.get(matiere).size(); i++) {
				moyenne += hashMat.get(matiere).get(i);
			}
			moyenne = moyenne / hashMat.get(matiere).size();
			ligne.add(df.format(moyenne));
			ligne.add(df.format(mapMatieresMoyennesClasse.get(matiere)));
			ligne.add("Votre remarque");
			notes.add(ligne);
		}
	}

	// Seul la colonne n4 qui correspond a la remarque du professeur est
	// editable
	@Override
	public boolean isCellEditable(int iRowIndex, int iColumnIndex) {
		if (iColumnIndex == 4)
			return true;
		else
			return false;
	}

	@Override
	public int getRowCount() {
		if (notes == null) {
			return 0;
		} else {
			return notes.size();
		}

	}

	@Override
	public int getColumnCount() {
		if (entetes == null) {
			return 0;
		} else {
			return entetes.size();
		}
	}

	@Override
	public String getColumnName(int columnIndex) {
		return entetes.get(columnIndex);
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		if (rowIndex > getRowCount() || columnIndex > getColumnCount()) {
			return "";
		} else {
			return notes.get(rowIndex).get(columnIndex);
		}
	}

	// Seule une modification sur la colonne 4 sera prise en compte et ajoutee a
	// la liste
	@Override
	public void setValueAt(Object aValue, int row, int column) {
		if (column == 4) {
			notes.get(row).set(column, (String) aValue);
		}
	}
}
